﻿using UnityEngine;
using System.Collections;

public class GridScript : MonoBehaviour {

	int[][][] grid;
	int[][][] tempGrid;
	bool initialized = false;
	int xmin, xmax;
	ExpandScript expandScript;
	PuzzleScript puzzleScript;

	// Use this for initialization
	void Start () {
		expandScript = gameObject.GetComponent<ExpandScript> ();
		puzzleScript = gameObject.GetComponent<PuzzleScript> ();
	}
	
	// Update is called once per frame
	void Update () {

	}

	public void updateGrid(ArrayList shapes){
		if (!initialized) {
			initializeGrid (shapes);
		} else {
			foreach (GameObject shape in shapes) {
				ShapeScript shapeScript = shape.GetComponent<ShapeScript> ();
				foreach (Vector3 cube in shapeScript.cubes) {
					grid [(int)cube.x-1] [(int)cube.y-1] [(int)cube.z-1] = shapeScript.getId ();
				}
			}
		}
	}

	public void removeShape(int id){
		for (int i = 0; i<grid.Length; i++) {
			for(int j = 0; j<grid[0].Length; j++){
				for(int k = 0; k<grid[0][0].Length; k++){
					if(grid[i][j][k]==id){
						grid[i][j][k] = -1;
					}
				}
			}
		}
	}

	public void doCollapseChanges(){
		while (true) {
			Debug.Log ("collapse1");
			bool repeat = setCollapsedShape(puzzleScript.getFirstDirection());
			//Debug.Log ("whileloop " + repeat);
			if (!expandScript.expandedState) {
				expandScript.setContracting();
			}
			if(!repeat)break;

		}
		while (true) {
			Debug.Log ("collapse2");
			bool repeat = setCollapsedShape(puzzleScript.getSecondDirection());
			//Debug.Log ("whileloop " + repeat);
			if (!expandScript.expandedState) {
				expandScript.setContracting();
			}
			if(!repeat)break;
		}
		while (true) {
			Debug.Log ("collapse3");
			bool repeat = setCollapsedShape(puzzleScript.getThirdDirection());
			//Debug.Log ("whileloop " + repeat);
			if (!expandScript.expandedState) {
				expandScript.setContracting();
			}
			if(!repeat)break;
		}
	}

	bool setCollapsedShape(int direction){
		bool breaking = false;
		GameObject shape = null;
		int shapeId = -1, moveDir = 0;
		Debug.Log ("Direction: " + direction);
		switch(direction){
		case 1:
			//Debug.Log (grid.Length);
			for (int i = 0; i<grid.Length; i++) {
				for(int j = 0; j<grid[0].Length; j++){
					for(int k = 0; k<grid[0][0].Length; k++){
						if(i==0){
							if(grid[i+1][j][k] == -1 && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								moveDir = -1;
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x] [(int)cube.y-1] [(int)cube.z-1] != -1){
										breaking = false;
									}
								}
								break;
							}
						}else if(i==grid.Length-1){
							if(grid[i-1][j][k] == -1 && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								moveDir = 1;
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-2] [(int)cube.y-1] [(int)cube.z-1] != -1){
										breaking = false;
									}
								}
								break;
							}
						}else{
							if((grid[i-1][j][k] == -1 || grid[i+1][j][k] == -1) && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								moveDir = getMoveDirection(shape, direction);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-(moveDir==1?2:0)] [(int)cube.y-1] [(int)cube.z-1] != -1){
										breaking = false;
									}
								}
								break;
							}
						}
					}
					if(breaking)break;
				}
				if(breaking)break;
			}
			if (breaking) {
				ShapeScript script = shape.GetComponent<ShapeScript> ();
				Vector3 initial = script.getInitialPosition ();
				initial.x -= (1*moveDir);
				script.setInitialPosition (initial);
				for(int i = 0; i<script.cubes.Length; i++){
					initial = script.cubes[i];
					grid [(int)initial.x-1] [(int)initial.y-1] [(int)initial.z-1] = -1;
					initial.x -= (1*moveDir);
					script.cubes[i] = initial;
					grid [(int)initial.x-1] [(int)initial.y-1] [(int)initial.z-1] = shapeId;
				}
				expandScript.getShapes()[expandScript.getShapeIndexById(shapeId)] = shape;

				updateGrid(expandScript.getShapes());
				return true;
			}
			break;
			//return false;
		case 2:
			//Debug.Log (grid.Length);
			for (int i = 0; i<grid.Length; i++) {
				for(int j = 0; j<grid[0].Length; j++){
					for(int k = 0; k<grid[0][0].Length; k++){
						if(j==0){
							if(grid[i][j+1][k] == -1 && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								moveDir = -1;
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-1] [(int)cube.y] [(int)cube.z-1] != -1){
										breaking = false;
									}
								}
								break;
							}
						}else if(j==grid[0].Length-1){
							if(grid[i][j-1][k] == -1 && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								moveDir = 1;
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-1] [(int)cube.y-2] [(int)cube.z-1] != -1){
										breaking = false;
									}
								}
								break;
							}
						}else{
							if((grid[i][j-1][k] == -1 || grid[i][j+1][k] == -1) && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								moveDir = getMoveDirection(shape, direction);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-1] [(int)cube.y-(moveDir==1?2:0)] [(int)cube.z-1] != -1){
										breaking = false;
									}
								}
								break;
							}
						}
					}
					if(breaking)break;
				}
				if(breaking)break;
			}
			if (breaking) {
				ShapeScript script = shape.GetComponent<ShapeScript> ();
				Vector3 initial = script.getInitialPosition ();
				initial.y -= (1*moveDir);
				script.setInitialPosition (initial);
				for(int i = 0; i<script.cubes.Length; i++){
					initial = script.cubes[i];
					grid [(int)initial.x-1] [(int)initial.y-1] [(int)initial.z-1] = -1;
					initial.y -= (1*moveDir);
					script.cubes[i] = initial;
					grid [(int)initial.x-1] [(int)initial.y-1] [(int)initial.z-1] = shapeId;
				}
				expandScript.getShapes()[expandScript.getShapeIndexById(shapeId)] = shape;
				
				updateGrid(expandScript.getShapes());
				return true;
			}
			break;
			//return false;
		case 3:
			//Debug.Log (grid.Length);
			for (int i = 0; i<grid.Length; i++) {
				for(int j = 0; j<grid[0].Length; j++){
					for(int k = 0; k<grid[0][0].Length; k++){
						if(k==0){
							if(grid[i][j][k+1] == -1 && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								moveDir = -1;
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-1] [(int)cube.y-1] [(int)cube.z] != -1){
										breaking = false;
									}
								}
								break;
							}
						}else if(k==grid[0][0].Length-1){
							if(grid[i][j][k-1] == -1 && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								moveDir = 1;
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-1] [(int)cube.y-1] [(int)cube.z-2] != -1){
										breaking = false;
									}
								}
								break;
							}
						}else{
							if((grid[i][j][k-1] == -1 || grid[i][j][k+1] == -1) && grid[i][j][k] != -1){
								//Debug.Log("ghjklghjklhjklhgjk");
								shapeId = grid[i][j][k];
								shape = expandScript.getShapeById(shapeId);
								moveDir = getMoveDirection(shape, direction);
								breaking = true;
								foreach(Vector3 cube in shape.GetComponent<ShapeScript>().cubes){
									if(grid [(int)cube.x-1] [(int)cube.y-1] [(int)cube.z-(moveDir==1?2:0)] != -1){
										breaking = false;
									}
								}
								break;
							}
						}
					}
					if(breaking)break;
				}
				if(breaking)break;
			}
			if (breaking) {
				ShapeScript script = shape.GetComponent<ShapeScript> ();
				Vector3 initial = script.getInitialPosition ();
				initial.z -= (1*moveDir);
				script.setInitialPosition (initial);
				for(int i = 0; i<script.cubes.Length; i++){
					initial = script.cubes[i];
					grid [(int)initial.x-1] [(int)initial.y-1] [(int)initial.z-1] = -1;
					initial.z -= (1*moveDir);
					script.cubes[i] = initial;
					grid [(int)initial.x-1] [(int)initial.y-1] [(int)initial.z-1] = shapeId;
				}
				expandScript.getShapes()[expandScript.getShapeIndexById(shapeId)] = shape;
				updateGrid(expandScript.getShapes());
				return true;
			}
			break;
		}
		return false;
	}

	int getMoveDirection (GameObject shape, int direction){
		switch (direction) {
		case 1:
			if(shape.transform.position.x > expandScript.puzzleCenter.x){
				return 1;
			}else{
				return -1;
			}
		case 2:
			if(shape.transform.position.y > expandScript.puzzleCenter.y){
				return 1;
			}else{
				return -1;
			}
		case 3:
			if(shape.transform.position.z > expandScript.puzzleCenter.z){
				return 1;
			}else{
				return -1;
			}
		}
		return 1;
	}

	void initializeGrid(ArrayList shapes){
		float x=Mathf.NegativeInfinity, y=Mathf.NegativeInfinity, z=Mathf.NegativeInfinity;
		float xmin=Mathf.Infinity, ymin=Mathf.Infinity, zmin=Mathf.Infinity;
		foreach (GameObject shape in shapes) {
			if(shape.transform.position.x > x){
				x = shape.transform.position.x;
			}
			if(shape.transform.position.y > y){
				y = shape.transform.position.y;
			}
			if(shape.transform.position.z > z){
				z = shape.transform.position.z;
			}
			if(shape.transform.position.x < xmin){
				xmin = shape.transform.position.x;
			}
			if(shape.transform.position.y < ymin){
				ymin = shape.transform.position.y;
			}
			if(shape.transform.position.z < zmin){
				zmin = shape.transform.position.z;
			}
		}
		int toX = Mathf.RoundToInt(x - xmin), toY = Mathf.RoundToInt(y - ymin), toZ = Mathf.RoundToInt(z - zmin);
		grid = new int[toX+1][][];
		for (int i = 0; i<toX+1; i++) {
			grid[i] = new int[toY+1][];
			for (int j = 0; j<toY+1; j++) {
				grid[i][j] = new int[toZ+1];
			}
		}
		for (int i = 0; i<grid.Length; i++) {
			for(int j = 0; j<grid[0].Length; j++){
				for(int k = 0; k<grid[0][0].Length; k++){
					grid[i][j][k] = -1;
				}
			}
		}
		initialized = true;
		updateGrid (shapes);
	}

	public bool isGridInitialized(){
		return initialized;
	}
}
